package com.image;

import org.bytedeco.javacpp.opencv_core;
import org.bytedeco.javacpp.opencv_imgcodecs;
import org.bytedeco.javacpp.opencv_imgproc;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.io.File;

public class OpenCV {

    public static void main(String[] args) {
        clarityException(new File("D:\\Desktop\\5.png"));
    }

    /**
     * javacv 检测图片清晰度
     * 标准差越大说明图像质量越好
     */
    public static void clarityException(File jpegFile) {
        String path = "E:\\test\\";
        opencv_core.Mat srcImage = opencv_imgcodecs.imread(jpegFile.getAbsolutePath());
        opencv_core.Mat dstImage = new opencv_core.Mat();
        //转化为灰度图
        opencv_imgproc.cvtColor(srcImage, dstImage, opencv_imgproc.COLOR_BGR2GRAY);
        //在gray目录下生成灰度图片
        opencv_imgcodecs.imwrite(path + "gray-" + jpegFile.getName(), dstImage);

        opencv_core.Mat laplacianDstImage = new opencv_core.Mat();
        //阈值太低会导致正常图片被误断为模糊图片，阈值太高会导致模糊图片被误判为正常图片
        opencv_imgproc.Laplacian(dstImage, laplacianDstImage, opencv_core.CV_64F);
        //在laplacian目录下升成经过拉普拉斯掩模做卷积运算的图片
        opencv_imgcodecs.imwrite(path + "laplacian-" + jpegFile.getName(), laplacianDstImage);

        //矩阵标准差
        opencv_core.Mat stddev = new opencv_core.Mat();

        //求矩阵的均值与标准差
        opencv_core.meanStdDev(laplacianDstImage, new opencv_core.Mat(), stddev);
        // ((全部元素的平方)的和)的平方根
        // double norm = Core.norm(laplacianDstImage);
        // System.out.println("\n矩阵的均值：\n" + mean.dump());
        System.out.println(jpegFile.getName() + "矩阵的标准差：\n" + stddev.createIndexer().getDouble());
        // System.out.println(jpegFile.getName()+"平方根：\n" + norm);
    }

    /**
     * opencv 检测图片颜色
     */
    public static void imageColor(File jpegFile) {
        Mat srcImage = Imgcodecs.imread(jpegFile.getAbsolutePath());
        Mat dstImage = new Mat();
        Imgproc.cvtColor(srcImage, dstImage, Imgproc.COLOR_BGR2HSV);
        int i = 0, j = 0;
        loop:
        for (i = 0; i < dstImage.rows(); i++) {
            for (j = 0; j < dstImage.cols(); j++) {
                //在计算过程中，考虑128为亮度均值点
                double[] colorVec = dstImage.get(i, j);
                int x = (int) dstImage.get(i, j)[0];
                if ((colorVec[0] >= 0 && colorVec[0] <= 180)
                        && (colorVec[1] >= 0 && colorVec[1] <= 255)
                        && (colorVec[2] >= 0 && colorVec[2] <= 46)) {
                    continue;
                } else if ((colorVec[0] >= 0 && colorVec[0] <= 180)
                        && (colorVec[1] >= 0 && colorVec[1] <= 43)
                        && (colorVec[2] >= 46 && colorVec[2] <= 220)) {
                    continue;
                } else if ((colorVec[0] >= 0 && colorVec[0] <= 180)
                        && (colorVec[1] >= 0 && colorVec[1] <= 30)
                        && (colorVec[2] >= 221 && colorVec[2] <= 255)) {
                    continue;
                } else {
                    System.out.println("彩色图像");
                    break loop;
                }
            }
        }
        if (i == dstImage.rows() && j == dstImage.cols()) {
            System.out.println("黑白图像");
        }
    }

    /**
     * 原理说明：计算图片在灰度图上的均值和方差，当存在亮度异常时，均值会偏离均值点（可以假设为128），方差也会偏小；通过计算灰度图的均值和方差，就可评估图像是否存在过曝光或曝光不足。
     * opencv 检测图片亮度
     * brightnessException 计算并返回一幅图像的色偏度以及，色偏方向
     * cast 计算出的偏差值，小于1表示比较正常，大于1表示存在亮度异常；当cast异常时，da大于0表示过亮，da小于0表示过暗
     * 返回值通过cast、da两个引用返回，无显式返回值
     */
    public static Integer brightnessException(File jpegFile) {
        Mat srcImage = Imgcodecs.imread(jpegFile.getAbsolutePath());
        Mat dstImage = new Mat();
        // 将RGB图转为灰度图
        Imgproc.cvtColor(srcImage, dstImage, Imgproc.COLOR_BGR2GRAY);
        float a = 0;
        int Hist[] = new int[256];
        for (int i = 0; i < 256; i++) {
            Hist[i] = 0;
        }
        for (int i = 0; i < dstImage.rows(); i++) {
            for (int j = 0; j < dstImage.cols(); j++) {
                //在计算过程中，考虑128为亮度均值点
                a += (float) (dstImage.get(i, j)[0] - 128);
                int x = (int) dstImage.get(i, j)[0];
                Hist[x]++;
            }
        }
        float da = a / (float) (dstImage.rows() * dstImage.cols());
        System.out.println(da);
        float D = Math.abs(da);
        float Ma = 0;
        for (int i = 0; i < 256; i++) {
            Ma += Math.abs(i - 128 - da) * Hist[i];
        }
        Ma /= (float) ((dstImage.rows() * dstImage.cols()));
        float M = Math.abs(Ma);
        float K = D / M;
        float cast = K;
        System.out.printf("亮度指数： %f\n", cast);
        if (cast >= 1) {
            System.out.printf("亮度：" + da);
            if (da > 0) {
                System.out.printf("过亮\n");
                return 2;
            } else {
                System.out.printf("过暗\n");
                return 1;
            }
        } else {
            System.out.printf("亮度：正常\n");
            return 0;
        }
    }

}


